#ifndef __CLI_PARSE__TESSELATION_H__
#define __CLI_PARSE__TESSELATION_H__

#include "../../Epsilon/Epsilon3D/OpenGL.h"
#include "../../Epsilon/STL.h"

#include <algorithm>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <iterator>
#include <stdexcept>
#include <vector>

#include "../../Epsilon/types.h"            /* CALLBACK */
#include "../../Epsilon/Epsilon3D/driver.h" // e3dRenderStates
using namespace e3dRenderStates;

namespace tess
{
	struct Vertex
	{	
		Vertex (float x, float y, float z) : x (x),
                                         y (y),
                                         z (z)  { };
	
		float x,
           y,
            z;
	};
	
	template < typename CharT, typename CharTraitsT >
	std::basic_ostream< CharT, CharTraitsT > & operator<<(std::basic_ostream< CharT, CharTraitsT > & stream, Vertex const & vertex)
	{
		return stream << '{' << vertex.x << ", " << vertex.y << ", " << vertex.z << '}';
	}
	
	class Polygon
	{
		public:
               Polygon (PrimitiveType type) : type_ (type) { }
      virtual ~Polygon (void);

			std::vector< Vertex >::const_iterator         begin  (void) const;
			std::vector< Vertex >::const_iterator         end    (void) const;

      std::vector< Vertex >::const_reverse_iterator rbegin (void) const;
			std::vector< Vertex >::const_reverse_iterator rend   (void) const;

			void push_back (Vertex const& vertex);
      void clear     (void);

      inline PrimitiveType type (void) const { return type_; };

			void render (void) const; // DO NOT USE
	
		private:
			PrimitiveType         type_;
			std::vector< Vertex > vertices_;
	};
	
	template < typename CharT, typename CharTraitsT >
	std::basic_ostream< CharT, CharTraitsT > & operator<<(std::basic_ostream< CharT, CharTraitsT > & stream, Polygon const & polygon);
	
	GLUtesselator* newTesselator (void);
	
	void CALLBACK tesselationBeginPrimitive  (GLenum   type);
	void CALLBACK tesselationVertex          (void*    vertexData);
	void CALLBACK tesselationEdge            (bool     edgeFlag);
	void CALLBACK tesselationCombineVertices (GLdouble coords     [3],
                                            void*    vertexData [4],
                                            GLfloat  weights    [4],
                                            void**   outData);
	void CALLBACK tesselationError           (GLenum   error);

  void InitTesselator (bool triangles_only = true);

  void Begin (void);
  bool End   (void);

  void BeginContour (void);
  void EndContour   (void);

  void AddVertex (float x, float y);

  void Invalidate (void);

  // Temp hack
  void Holes (void);


  void Clear (void);
  std::vector< Polygon >::const_iterator Erase (std::vector< Polygon >::iterator& iter);

  std::vector< Polygon >::iterator GetFirst (void);
  std::vector< Polygon >::iterator GetLast  (void);
  

  //
  // Internal use only
  //
  void reset (void);

  
  /*
  extern std::vector< Polygon > inputPolygons;
	extern std::vector< Polygon > tesselatedPolygons;
	extern std::deque<  Vertex  > inputVertices;

  extern GLUtesselator* tesselator;

  extern bool invalid;
  */
}

#endif /* __CLI_PARSE__TESSELATION_H__ */
